package it.polimi.swKnights.SWIMv2.ejb.messages;

import it.polimi.swKnights.SWIMv2.ejb.authentication.LoginHandlerLocal;
import it.polimi.swKnights.SWIMv2.ejb.entityBeans.PrivateMessage;
import it.polimi.swKnights.SWIMv2.ejb.entityBeans.ReceivedMessages;
import it.polimi.swKnights.SWIMv2.ejb.entityBeans.ReceivedMessagesPK;
import it.polimi.swKnights.SWIMv2.ejb.entityBeans.RegisteredUser;
import it.polimi.swKnights.SWIMv2.ejb.exceptions.InvalidLoginPrivilegesException;
import it.polimi.swKnights.SWIMv2.ejb.exceptions.LoginFailException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 * Session Bean implementation class MessageHandlerBean
 */
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class MessageHandlerBean implements MessageHandlerBeanRemote
{

	@PersistenceContext(unitName = "swimv2-manager")
	private EntityManager manager;

	@EJB
	private LoginHandlerLocal loginManager;

	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void persistMessage(String message, List<String> recipients,
			String sender, String password, String object)
			throws LoginFailException, InvalidLoginPrivilegesException
	{
		if (!sender.equals("guest"))
		{
			this.loginManager.checkLoginDataAndPrivileges(sender, password,
					RegisteredUser.class);
			this.loginManager.checkBanned(sender);
		}
		PrivateMessage sentMessage = new PrivateMessage(sender);

		RegisteredUser senderUser = manager.find(RegisteredUser.class, sender);

		sentMessage.setSender(senderUser);
		sentMessage.setText(message);
		sentMessage.setObject(object);
		manager.persist(sentMessage);

		for (String recipient : recipients)
		{
			RegisteredUser _recipient = manager.getReference(
					RegisteredUser.class, recipient);
			ReceivedMessages bridge = new ReceivedMessages(sentMessage,
					_recipient);

			manager.persist(bridge);

		}

	}

	@Override
	public Map<String, String> getMessage(String userName, String password,
			int id) throws LoginFailException, InvalidLoginPrivilegesException
	{
		this.loginManager.checkLoginDataAndPrivileges(userName, password,
				RegisteredUser.class);
		String findMessagesQuery = ""
				+ "SELECT pm, rm.messageRead  FROM PrivateMessage AS pm, ReceivedMessages as rm "
				+ "WHERE rm.recipient = :userName AND pm.id = rm.messageID "
				+ "AND pm.id = :id ";

		Query query = this.manager.createQuery(findMessagesQuery);
		query.setParameter("userName", userName);
		query.setParameter("id", id);
		
		List<Map<String, String>> messageList = this.setAttribute(query.getResultList());
		
		if(messageList.isEmpty())
		{
			return new HashMap<String, String>();
		}
		else
		{
			return messageList.get(0);
		}

	}

	/**
	 * returns all the messages received by the user passed as parameter.
	 * messages are returned as a list of map
	 * 
	 * @param String
	 *            user -> the username of the recipient
	 * @throws InvalidLoginPrivilegesException
	 * @throws LoginFailException
	 */
	@Override
	public List<Map<String, String>> getMessagesFromUser(String userName,
			String password) throws LoginFailException,
			InvalidLoginPrivilegesException
	{
		this.loginManager.checkLoginDataAndPrivileges(userName, password,
				RegisteredUser.class);
		String findMessagesQuery = ""
				+ "SELECT pm, rm.messageRead FROM PrivateMessage AS pm, ReceivedMessages as rm "
				+ "WHERE rm.recipient = :userName AND pm.id = rm.messageID "
				+ "ORDER BY pm.date desc";

		Query query = this.manager.createQuery(findMessagesQuery);
		query.setParameter("userName", userName);
		
		
		List<?> results = query.getResultList();
		return this.setAttribute(results);
		
		
		

		

	}

	@Override
	public int getMessagesCountFromUser(String userName, String password)
	{
		try
		{
			this.loginManager.checkLoginDataAndPrivileges(userName, password,
					RegisteredUser.class);
			String findMessagesQuery = ""
					+ "SELECT COUNT(rm.messageRead) FROM  ReceivedMessages AS rm "
					+ "WHERE rm.recipient = :userName AND rm.messageRead=false";

			Query query = this.manager.createQuery(findMessagesQuery);
			query.setParameter("userName", userName);
			Long lResult = (Long) (query.getSingleResult());

			long l = lResult;
			int res = (int) l;

			return res;
		}
		catch (LoginFailException e)
		{
		}
		catch (InvalidLoginPrivilegesException e)
		{
		}

		return 0;

	}

	/**
	 * returns all the messages sent by the user passed as parameter represented
	 * as a list of map
	 * 
	 * @param String
	 *            user -> the user we want sent messages from
	 * @throws InvalidLoginPrivilegesException
	 * @throws LoginFailException
	 */
	@Override
	public List<Map<String, String>> getMessageSentByUser(String user,
			String password) throws LoginFailException,
			InvalidLoginPrivilegesException

	{
		this.loginManager.checkLoginDataAndPrivileges(user, password,
				RegisteredUser.class);
		RegisteredUser sender = this.manager.find(RegisteredUser.class, user);

		Query query = this.manager
				.createQuery("SELECT m FROM PrivateMessage m WHERE m.sender=:sender ORDER BY m.date desc");
		query.setParameter("sender", sender);

		List<PrivateMessage> sentMessages = new ArrayList<PrivateMessage>();

		for (Object o : query.getResultList())
		{

			sentMessages.add((PrivateMessage) o);
		}

		return this.setSentMessagesAttribute(sentMessages);

	}

	@Override
	public void setMessageAsRead(String recipient, String password, int id)
			throws LoginFailException, InvalidLoginPrivilegesException
	{

		this.loginManager.checkLoginDataAndPrivileges(recipient, password,
				RegisteredUser.class);

		ReceivedMessagesPK key = new ReceivedMessagesPK();

		key.setMessageID(id);
		key.setRecipient(recipient);

		ReceivedMessages message = this.manager.find(ReceivedMessages.class,
				key);
		message.setMessageRead(true);

		this.manager.persist(message);
	}

	private String buildSentUserName(List<String> users)
	{
		StringBuilder finalUser = new StringBuilder();

		for (String s : users)
		{
			finalUser.append(s);
			finalUser.append(",");
		}

		finalUser.replace(finalUser.length() - 1, finalUser.length(), "");
		return finalUser.toString();

	}

	private List<Map<String, String>> setSentMessagesAttribute(
			List<PrivateMessage> messageList)
	{
		List<Map<String, String>> abstractSentMessages = new ArrayList<Map<String, String>>();
		for (PrivateMessage pm : messageList)
		{

			Map<String, String> message = new HashMap<String, String>();
			message.put("id", "" + pm.getId());
			message.put("date", pm.getDate().toString());
			String sentToUsers = buildSentUserName(pm.getRecipients());
			message.put("object", pm.getObject());
			message.put("sent_to", sentToUsers);
			message.put("text", pm.getText());

			abstractSentMessages.add(message);
		}

		return abstractSentMessages;
	}

	private List<Map<String, String>> setAttribute(List<?> messageList)
	{
		List<Map<String, String>> abstractMessageList = new ArrayList<Map<String, String>>();

		for (Object o : messageList)
		{
			Object[] objectArray = (Object[]) o;
			PrivateMessage m = (PrivateMessage) objectArray[0];
			Boolean isRead = (Boolean) objectArray[1];

			Map<String, String> attributeMap = new HashMap<String, String>();
			attributeMap.put("id", "" + m.getId());
			attributeMap.put("date", m.getDate().toString());
			attributeMap.put("sender", m.getSent_By());
			attributeMap.put("object", m.getObject());
			attributeMap.put("text", m.getText());
			attributeMap.put("read", isRead.toString());

			abstractMessageList.add(attributeMap);

		}

		return abstractMessageList;
	}

}
